package com.utils.basejava.NIO;


import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/*
 * 一、通道（Channel）：用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据，因此需要配合缓冲区进行传输。
 * 
 * 二、通道的主要实现类
 * 	java.nio.channels.Channel 接口：
 * 		|--FileChannel
 * 		|--SocketChannel
 * 		|--ServerSocketChannel
 * 		|--DatagramChannel
 * 
 * 三、获取通道
 * 1. Java 针对支持通道的类提供了 getChannel() 方法
 * 		本地 IO：
 * 		FileInputStream/FileOutputStream
 * 		RandomAccessFile
 * 
 * 		网络IO：
 * 		Socket
 * 		ServerSocket
 * 		DatagramSocket
 * 		
 * 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
 * 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
 * 
 * 四、通道之间的数据传输
 * transferFrom()
 * transferTo()
 * 
 * 五、分散(Scatter)与聚集(Gather)
 * 分散读取（Scattering Reads）：将通道中的数据分散到多个缓冲区中
 * 聚集写入（Gathering Writes）：将多个缓冲区中的数据聚集到通道中
 * 
 * 六、字符集：Charset
 * 编码：字符串 -> 字节数组
 * 解码：字节数组  -> 字符串
 */
public class TestChannel {
	
	
	//通道之间的数据传输（使用直接缓冲区）
	
	public void test1() throws IOException {
		FileChannel inChannel = FileChannel.open(Paths.get("C:\\\\Users\\\\XR-8\\\\Desktop\\\\忧伤.jpg"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE_NEW);
		
//		inChannel.transferTo(0, inChannel.size(), outChannel);
		outChannel.transferFrom(inChannel, 0, inChannel.size());
		
		inChannel.close();
		outChannel.close(); 
	}
	
	
	//使用直接缓冲区完成文件的复制（内存映射文件）
	
	public void test2() throws IOException{
		FileChannel inChannel = FileChannel.open(Paths.get("C:\\\\Users\\\\XR-8\\\\Desktop\\\\忧伤.jpg"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE_NEW);
		
		//内存映射文件
		MappedByteBuffer inMapperBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
		MappedByteBuffer outMapperBuf = outChannel.map(MapMode.READ_WRITE, 0, outChannel.size());
		
		//对直接缓冲区进行数据的读写操作
		byte[] dst = new byte[inMapperBuf.limit()];
		inMapperBuf.put(dst);
		outMapperBuf.get(dst);
		
		inChannel.close();
		outChannel.close();
		
		//从内存刷到磁盘上，交给操作系统去处理
	}
	
	
	//利用通道完成文件的复制（非直接缓冲区）
	
	public void test3() {
		long start = System.currentTimeMillis();
		FileInputStream fis = null;
		FileOutputStream fos = null;
		FileChannel inChannel = null;
		FileChannel outChannel = null;
		try {
			fis = new FileInputStream("C:\\Users\\XR-8\\Desktop\\忧伤.jpg");
			fos = new FileOutputStream("2.jpg");
			
			//获取通道
			inChannel = fis.getChannel();
			outChannel = fos.getChannel();
			
			//TODO 以上获取通道方式可更改为open()的方式。不要再获取流。JDK1.7以上版本提供（NIO2）
			
			//分配指定大小的缓冲区
			ByteBuffer dst = ByteBuffer.allocate(1024);
			
			while(inChannel.read(dst)!=-1) {//将通道中的数据存入缓冲区中.将文件中的数据通过通道存入缓冲区中
				dst.flip();//切换读取数据的模式
				//将缓冲区中的数据写入通道中.将缓冲区中的数据通过通道写入文件中
				outChannel.write(dst);
				dst.clear();//清空缓冲区
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(outChannel!=null){
				try {
					outChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(inChannel!=null) {
				try {
					inChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fos!=null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fis!=null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
		}
		
		long end = System.currentTimeMillis();
		System.out.println("耗费时间："+(end-start));
	}

	//分散和聚集
	
	public void test4() throws IOException {
		@SuppressWarnings("resource")
		RandomAccessFile raf1 = new RandomAccessFile("C:\\\\Users\\\\XR-8\\\\Desktop\\\\1.txt", "rw");
		
		//1、获取通道
		FileChannel channel1 = raf1.getChannel();
		
		//2、分配指定大小的缓冲区
		ByteBuffer buf1 = ByteBuffer.allocate(10);
		ByteBuffer buf2 = ByteBuffer.allocate(1024);
		
		//3、分散读取
		ByteBuffer[] bufs = {buf1,buf2};
		channel1.read(bufs);
		
		for (ByteBuffer byteBuffer : bufs) {
			byteBuffer.flip();
		}
		
		System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
		System.out.println("---------------------");
		System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
		
		//4、聚集写入
		RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
		FileChannel channel2 = raf2.getChannel();
		
		channel2.write(bufs);
	}
	
	
	public void test5() {
		Map<String, Charset> map = Charset.availableCharsets();
		
		Set<Entry<String, Charset>> set = map.entrySet();
		
		for (Entry<String, Charset> entry : set) {
			System.out.println(entry.getKey() + "=" + entry.getValue());
		}
	}
	
	
	//字符集
	
	public void test6() throws IOException {
		Charset cs1 = Charset.forName("GBK");
		//获取编码器
		CharsetEncoder newEncoder = cs1.newEncoder();
		//获取解码器
		CharsetDecoder newDecoder = cs1.newDecoder();
		
		CharBuffer cbuf = CharBuffer.allocate(1024);
		cbuf.put("尚硅谷威武！");
		cbuf.flip();
		
		//编码
		ByteBuffer bbuf = newEncoder.encode(cbuf);
//		bbuf.flip();
		for (int i = 0; i < 12; i++) {
			System.out.println(bbuf.get());
		}
		
		//解码
		bbuf.flip();
		CharBuffer cbuf2 = newDecoder.decode(bbuf);
		System.out.println(cbuf2.toString());
		
		System.out.println("------------------------------");
		
		//乱码
		Charset cs2 = Charset.forName("UTF-8");
		bbuf.flip();
		CharBuffer cbuf3 = cs2.decode(bbuf);
		System.out.println(cbuf3.toString());
		
	}
	
}
